package org.example.hrpc.rpc.core;

import org.apache.curator.utils.ThreadUtils;
import org.example.hrpc.common.URL;
import org.example.hrpc.common.constant.CommonConstant;
import org.example.hrpc.common.exception.RpcException;
import org.example.hrpc.common.utils.ClassUtils;
import org.example.hrpc.common.utils.ReflectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;

/**
 * This Invoker works on provider side, delegates RPC to interface implementation.
 * 该Invoker工作在provider端，委托RPC给接口实现类
 */
public abstract class AbstractProxyInvoker<T> implements Invoker<T> {
    Logger logger = LoggerFactory.getLogger(AbstractProxyInvoker.class);

    // RPC实现类
    private final T proxy;

    private final Class<T> type;

    private final URL url;

    public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
        if (proxy == null) {
            throw new IllegalArgumentException("proxy == null");
        }
        if (type == null) {
            throw new IllegalArgumentException("interface == null");
        }
        if (!type.isInstance(proxy)) {
            throw new IllegalArgumentException(proxy.getClass().getName() + " not implement interface " + type);
        }
        this.proxy = proxy;
        this.type = type;
        this.url = url;
    }

    @Override
    public Class<T> getInterface() {
        return type;
    }


    public URL getUrl() {
        return url;
    }



    public void destroy() {
    }

    @Override
    public Result invoke(Invocation invocation) throws RpcException {
        try {
            String methodName = invocation.getMethodName();
            Class<?>[] paramsType = invocation.getParameterTypes();
            Object[] args = invocation.getArguments();
            // 这里 需要判断是否是泛化实现
            if("invoke".equals(invocation.getMethodName()) && Arrays.equals(CommonConstant.GENERIC_PARAMETER_TYPES, invocation.getParameterTypes())) {
                logger.info(Arrays.toString(args));
                // interfaceName
                 String interfaceName = (String) args[0];
                // methodName
                methodName = (String) args[1];
                // param
                String[] paramsTypeString= (String[]) args[2];
                logger.info(Arrays.toString(paramsTypeString));
                Class<?>[] tmpParamsType = new Class<?>[paramsTypeString.length];
                for(int i=0;i<paramsTypeString.length;i++) {
                    tmpParamsType[i] = ClassUtils.forName(paramsTypeString[i]);
                }
                paramsType = tmpParamsType;
                // args
                args = (Object[]) args[3];
                logger.info("interfaceName: {}, methodName: {}, paramsType: {}, args: {}", interfaceName, methodName, Arrays.toString(paramsType), Arrays.toString(args));
            }
            // Object value = doInvoke(proxy, invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
            Object value = doInvoke(proxy, methodName, paramsType, args);
            AppResponse appResponse = new AppResponse();
            appResponse.setValue(value);
            return appResponse;
        } catch (InvocationTargetException e) {
//            if (RpcContext.getContext().isAsyncStarted() && !RpcContext.getContext().stopAsync()) {
//                logger.error("Provider async started, but got an exception from the original method, cannot write the exception back to consumer because an async result may have returned the new thread.", e);
//            }
            return AsyncRpcResult.newDefaultAsyncResult(null, e.getTargetException(), invocation);
        } catch (Throwable e) {
            throw new RpcException("Failed to invoke remote proxy method " + invocation.getMethodName() + " to " + getUrl() + ", cause: " + e.getStackTrace(), e);
        }
    }


    protected abstract Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable;

    @Override
    public String toString() {
        return getInterface() + " -> " + (getUrl() == null ? " " : getUrl().toString());
    }

}

